/* + C O P Y R I G H T * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* See top source directory for COPYRIGHT.txt                              */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/* - C O P Y R I G H T * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "athd.h"

static int _athd_listen_sig_start();
static int _athd_listen_sig_stop();
static int _athd_listen_sig_shutdown();
static void * _athd_listen_thread_cleanup(void *ctx);
static apr_status_t _athd_listen_socket_setup();
static void _athd_listen_socket_shutdown();

static char *_host = NULL;
static int _dienow = 0;
static apr_pool_t *_poolsock = NULL;
static apr_pool_t *_poolhandler = NULL;
static apr_socket_t *_socket = NULL;
static apr_port_t _port = -1;
static apr_sockaddr_t *_addr = NULL;
static apr_int32_t _backlog = ATHD_MAX_CONN;
static int _sockpending = 0;

apr_status_t
athd_listen_thread_setup(apr_pool_t *pool)
{
    apr_status_t rv;
    athd_conf *conf = athd_conf_current_get();
    ASSERT(conf);
    if (conf->port > 0)
      {
	  if (!_poolsock)
	      apr_pool_create(&_poolsock, pool);
	  if (!_poolhandler)
	      apr_pool_create(&_poolhandler, pool);
	  _host = "localhost";
	  _port = conf->port;
	  rv = _athd_listen_socket_setup();
	  if (rv)
	    {
		athd_log_err(ATHD_CRIT, rv, "Listen Thread: Fatal error: ");
		_dienow = 1;
	    }
	  athd_sig_handler_register(_athd_listen_sig_start, 
				    "Listen Thread", ATHD_START);
	  athd_sig_handler_register(_athd_listen_sig_stop, 
				    "Listen Thread", ATHD_STOP);
	  athd_sig_handler_register(_athd_listen_sig_shutdown,
				    "Listen Thread", ATHD_SHUTDOWN);
      }
    else
      {
	  athd_log(ATHD_INFO, "Listen Thread: No port configured");
	  _dienow = 1;
      }
    return rv;
}

void *
athd_listen_thread(void *ctx)
{
    athd_conf *conf = ctx;
    int laststate, lasttype;
    struct sigaction action = { NULL } ;

    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &laststate);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &lasttype);
    pthread_cleanup_push((void *)_athd_listen_thread_cleanup, (void *)conf);

    action.sa_handler = athd_connection_reset;
    sigaction(SIGPIPE, &action, 0);

    if (!_dienow)
	athd_log(ATHD_INFO, "Listen Thread: Enter on [%s:%d]", _host, _port);
    for (; (!_dienow) ;)
      {
	  athd_cmd_handler *client;
	  pthread_t *thread;
	  apr_status_t rv;

	  client = athd_cmd_handler_create(_poolhandler);
	  if (athd_cmd_handler_sessions_active() < ATHD_MAX_CONN)
	    {
		rv = athd_cmd_io_accept(client, _socket);
		if (rv) 
		  {
		      athd_log(ATHD_WARN, "Listen Thread: accept error");
		      _dienow = 1;
		  }
	    }
	  else
	    {
		int sleepy = 3;
		athd_log(ATHD_WARN, 
			 "Listen Thread: Max cmd sessions (%d) reached, "
			 "sleeping %d seconds", ATHD_MAX_CONN, sleepy);
		apr_sleep(apr_time_from_sec(sleepy));
	    }
      }
    pthread_cleanup_pop(1);
}

static int
_athd_listen_sig_start()
{
    _athd_listen_socket_setup();
    return APR_SUCCESS;
}

static int
_athd_listen_sig_stop()
{
    _athd_listen_socket_shutdown();
    return APR_SUCCESS;
}

static int
_athd_listen_sig_shutdown()
{
    _athd_listen_socket_shutdown();
    return APR_SUCCESS;
}

static apr_status_t
_athd_listen_socket_setup()
{
    int rv;
    ASSERT(_poolsock);
    rv = apr_socket_create(&_socket, APR_INET, SOCK_STREAM, _poolsock);
    RV_OK = apr_socket_opt_set(_socket, APR_SO_REUSEADDR, 1);
    RV_OK = apr_socket_opt_set(_socket, APR_SO_KEEPALIVE, 1);
    RV_OK = apr_socket_opt_set(_socket, APR_SO_SNDBUF, 512);
    RV_OK = apr_socket_opt_set(_socket, APR_SO_RCVBUF, 512);
    RV_OK = apr_sockaddr_info_get(&_addr, _host, APR_UNSPEC, _port,
				  APR_IPV4_ADDR_OK, _poolsock);
    RV_OK = apr_socket_bind(_socket, _addr);
    RV_OK = apr_socket_listen(_socket, _backlog);
    return rv;
}

static void
_athd_listen_socket_shutdown()
{
    if (_socket)
	apr_socket_shutdown(_socket, APR_SHUTDOWN_READWRITE);
}
    
static void *
_athd_listen_thread_cleanup(void *ctx)
{
    athd_log(ATHD_INFO, "Listen Thread: Exit");
}
